/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "SnapshotRestore.h"
#include "log/Log.h"
#include "utils/PluginUtilities.h"

using namespace std;
using namespace Module;
using namespace PluginUtils;
namespace {
    const auto MODULE = "SnapshotRestore";
    constexpr int SLEEP_INTERVAL = 5;
    const string SNAPSHOT_RESTORE_IDLE = "0";
    const string SNAPSHOT_RESTORE_COMPLETE = "3";
    const string SNAPSHOT_RESTORE_ABORT = "4";
    /* pacific快照回滚状态参数 */
    const string SNAPSHOT_PACIFIC_ROLLBACK_NORMAL = "1";
    const string SNAPSHOT_PACIFIC_ROLLBACK_ROLLBACKING = "2";
    constexpr int QUERY_RETRY_TIME = 5;
    const int ROLL_BACK_ERR_CODE = 1073754141;
}


SnapshotRestore::~SnapshotRestore() {}

EXTER_ATTACK int SnapshotRestore::PrerequisiteJob()
{
    m_jobInfoPtr = dynamic_pointer_cast<AppProtect::RestoreJob>(m_jobCommonInfo->GetJobInfo());
    int ret = PrerequisiteJobInner();
    if (ret == Module::FAILED) {
        ReportJobDetails("plugin_execute_prerequisit_task_fail_label",
            JobLogLevel::type::TASK_LOG_ERROR, SubJobStatus::FAILED, m_jobProgress);
        HCP_Log(ERR, MODULE) << "Restore PrerequisiteJob failed jobId: " << m_jobInfoPtr->jobId << HCPENDLOG;
    }
    SetJobToFinish();
    return ret;
}

int SnapshotRestore::PrerequisiteJobInner()
{
    ReportJobDetails(EMPTY_LABEL, JobLogLevel::type::TASK_LOG_INFO, SubJobStatus::COMPLETED, NUMBER100);
    return Module::SUCCESS;
}

int SnapshotRestore::GenerateSubJob()
{
    m_jobInfoPtr = dynamic_pointer_cast<AppProtect::RestoreJob>(m_jobCommonInfo->GetJobInfo());
    m_jobId = m_jobInfoPtr->jobId;
    int ret = GenerateSubJobInner();
    if (ret == Module::FAILED) {
        ReportJobDetails("plugin_generate_subjob_fail_label",
            JobLogLevel::type::TASK_LOG_ERROR, SubJobStatus::FAILED, m_jobProgress);
        HCP_Log(ERR, MODULE) << "Restore GenerateSubJob failed,jobId is: " << m_jobInfoPtr->jobId << HCPENDLOG;
    }
    SetJobToFinish();
    return ret;
}

int SnapshotRestore::GenerateSubJobInner()
{
    HCP_Log(INFO, MODULE) << "Generate sub task, task id is " << m_jobId << HCPENDLOG;
    SubJob subJob;
    subJob.__set_jobId(m_jobId);
    subJob.__set_jobType(SubJobType::BUSINESS_SUB_JOB);
    subJob.__set_jobName("SnapshotRestore");
    subJob.__set_policy(ExecutePolicy::ANY_NODE); // LOCAL_NODE
    std::vector<SubJob> vec;
    vec.push_back(subJob);
    ActionResult result;

    JobService::AddNewJob(result, vec);
    if (result.code == Module::FAILED) {
        HCP_Log(INFO, MODULE) << "Finish to generate sub job, main task id is " << m_jobId << HCPENDLOG;
        return Module::FAILED;
    }
    ReportJobDetails(EMPTY_LABEL,
        JobLogLevel::type::TASK_LOG_INFO, SubJobStatus::COMPLETED, NUMBER100);
    HCP_Log(INFO, MODULE) << "Success to generate sub job, main task id is " << m_jobId << HCPENDLOG;
    return Module::SUCCESS;
}

int SnapshotRestore::ExecuteSubJob()
{
    m_jobInfoPtr = dynamic_pointer_cast<AppProtect::RestoreJob>(m_jobCommonInfo->GetJobInfo());
    m_subjobId = m_subJobInfo->subJobId;
    m_jobId = m_subJobInfo->jobId;
    int ret = ExecuteSubJobInner();
    if (ret == Module::FAILED) {
        ReportJobDetails("plugin_restore_subjob_fail_label",
            JobLogLevel::type::TASK_LOG_ERROR, SubJobStatus::FAILED, m_jobProgress);
        HCP_Log(ERR, MODULE) << "Snapshot Restore failed jobId: " << m_jobId <<
            " subjobid is:" << m_subjobId << HCPENDLOG;
    }
    SetJobToFinish();
    return ret;
}

int SnapshotRestore::ExecuteSubJobInner()
{
    HCP_Log(INFO, MODULE) << "Execute subJob jobid: " << m_jobId << "  subjobid: " << m_subjobId << HCPENDLOG;
    ActionResult result;
    SubJobDetails subJobDetails;
    LogDetail logDetail{};
    std::vector<LogDetail> logDetailList;

    if (InitJobInfo() != Module::SUCCESS) {
        HCP_Log(ERR, MODULE) << "Init device info fail, main task id is " << m_parentJobId << HCPENDLOG;
        return Module::FAILED;
    }

    if (InitDeviceInfo() != Module::SUCCESS) {
        HCP_Log(ERR, MODULE) << "Init device info fail, main task id is " << m_parentJobId << HCPENDLOG;
        return Module::FAILED;
    }

    if (IsAbortJob()) {
        ReportJobDetails("Snapshot restore abort",
            JobLogLevel::type::TASK_LOG_WARNING, SubJobStatus::ABORTED, m_jobProgress);
        return Module::FAILED;
    }

    if (CheckExistSnapshot() != Module::SUCCESS) {
        AddLogDetail(logDetail, "dme_databases_snapshot_not_exist_label", JobLogLevel::TASK_LOG_ERROR, m_snapshotName);
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, m_curProcess, 0, SubJobStatus::FAILED);
        HCP_Log(ERR, MODULE) << "snapshot don't exiset" << HCPENDLOG;
        return Module::FAILED;
    }
    std::string dtreeInfo;
    int ret = ExecuteSnapshotRestore(dtreeInfo);
    if (ret != Module::SUCCESS) {
        if (!dtreeInfo.empty()) {
            AddLogDetail(logDetail, "nas_plugin_snapshot_rollback_fail_label", JobLogLevel::TASK_LOG_ERROR);
            AddErrCode(logDetail, ret, dtreeInfo);
        } else {
            AddLogDetail(logDetail, "nas_plugin_snapshot_rollback_fail_label", JobLogLevel::TASK_LOG_ERROR);
            AddErrCode(logDetail, ret);
        }
        REPORT_LOG2AGENT(subJobDetails, result, logDetailList, logDetail, m_curProcess, 0, SubJobStatus::FAILED);
        return Module::FAILED;
    }
    ReportJobDetails(EMPTY_LABEL,
        JobLogLevel::type::TASK_LOG_INFO, SubJobStatus::COMPLETED, NUMBER100);
    return Module::SUCCESS;
}

int SnapshotRestore::WaitingSnapshotRestoreComplete()
{
    string rollbackStatus = SNAPSHOT_RESTORE_IDLE;
    string endTime;
    bool snapshotRollbackFlag = false;
    while (true) {
        ReportJobDetails(EMPTY_LABEL, JobLogLevel::type::TASK_LOG_INFO, SubJobStatus::RUNNING, NUMBER0);
        if (IsAbortJob()) {
            ReportJobDetails(EMPTY_LABEL, JobLogLevel::type::TASK_LOG_WARNING, SubJobStatus::ABORTED, m_jobProgress);
            HCP_Log(INFO, MODULE) << "The snapshot restoration task has been canceled." << HCPENDLOG;
            return Module::FAILED;
        }
        HCP_Log(INFO, MODULE) << "Enter WaitingSnapshotRestoreComplete........" << HCPENDLOG;
        if (QuerySnapshotRollBackStatus(QUERY_RETRY_TIME, rollbackStatus, endTime) != Module::SUCCESS) {
            return Module::FAILED;
        }
        // rollbackStatus状态为0,说明本次回滚结束
        if (GetVendorType(m_nasHost.vendorSubType) == Module::STORAGE_ENUM::DORADO) {
            if (rollbackStatus == SNAPSHOT_RESTORE_IDLE) {
                snapshotRollbackFlag = true;
                HCP_Log(INFO, MODULE) << "RevertStartTime："<< m_revertStartTime << " endTime:" << endTime << HCPENDLOG;
                break;
            }
        }
        if (GetVendorType(m_nasHost.vendorSubType) == Module::STORAGE_ENUM::FUSIONSTORAGE) {
            if (rollbackStatus == SNAPSHOT_PACIFIC_ROLLBACK_NORMAL) {
                snapshotRollbackFlag = true;
                HCP_Log(INFO, MODULE) << "RevertStartTime："<< m_revertStartTime << " endTime:" << endTime << HCPENDLOG;
                break;
            }
        }

        HCP_Log(INFO, MODULE) << "Waiting the snapshot recover to complete......." << HCPENDLOG;
        sleep(SLEEP_INTERVAL);
    }
    if (snapshotRollbackFlag) {
        sleep(NUMBER30); // 查询到endtime之后等待30s确保任务结束
    }
    return Module::SUCCESS;
}

int SnapshotRestore::QuerySnapshotRollBackStatus(const int& maxRetryTime, string& rollbackStatus, string& endTime)
{
    string snapshotId;
    string rollbackRate;

    int ret;
    for (int retryTime = 0; retryTime < maxRetryTime; ++retryTime) {
        ret = Module::FAILED;
        if (GetVendorType(m_nasHost.vendorSubType) == Module::STORAGE_ENUM::DORADO) {
            auto localDeviceControl = dynamic_pointer_cast<Module::DoradoNas>(m_localDeviceController);
            ret = localDeviceControl->QuerySnapshotRollBackStatusV3(
                m_deviceInfo.fileSystemId, m_snapshotName, rollbackStatus, endTime);
        }
        if (GetVendorType(m_nasHost.vendorSubType) == Module::STORAGE_ENUM::FUSIONSTORAGE) {
            auto localDeviceControl = dynamic_pointer_cast<Module::FusionStorageNas>(m_localDeviceController);
            ret = localDeviceControl->QuerySnapshotRollBackStatus(
                m_snapshotName, snapshotId, rollbackStatus, endTime);
        }
        if (ret == Module::SUCCESS) {
            HCP_Log(INFO, MODULE) << "The m_deviceInfo.fileSystemName is " << m_deviceInfo.fileSystemName
                << "  The snapshotId is " << snapshotId << "  The rollbackRate is " << rollbackRate
                << "  The rollbackStatus is " << rollbackStatus << "  The endTime is " << endTime << HCPENDLOG;
            return Module::SUCCESS;
        }
    }

    HCP_Log(ERR, MODULE) << "Query snapshot restoring status failed." << "errercode is : " << ret << HCPENDLOG;
    return ret;
}

int SnapshotRestore::ExecuteSnapshotRestore(std::string& dtreeInfo)
{
    HCP_Log(DEBUG, MODULE) << "Start to perform snapshot recovery." << HCPENDLOG;
    int ret;
    m_revertStartTime = PluginUtils::GetCurrentTimeInSeconds();
    if (GetVendorType(m_nasHost.vendorSubType) == Module::STORAGE_ENUM::DORADO) {
        auto localDeviceControl = dynamic_pointer_cast<Module::DoradoNas>(m_localDeviceController);
        Module::RestResult result;
        ret = localDeviceControl->RollBackBySnapShotIdWithVstoreId(m_snapshotName, m_snapshotId, m_vstoreId, result);
        if (result.errorCode == ROLL_BACK_ERR_CODE) {
            std::string suggestion = result.errSuggestion;
            std::string delimiter = "as follows:";
            // 找到待删除的dtree信息
            size_t pos = suggestion.find(delimiter);
            if (pos != std::string::npos) {
                std::string content = suggestion.substr(pos + delimiter.length());
                // 去掉空格
                content.erase(std::remove_if(content.begin(), content.end(), ::isspace), content.end());
                // 去掉句号
                content.erase(std::remove(content.begin(), content.end(), '.'), content.end());
                INFOLOG("Process content: %s", content.c_str());
                dtreeInfo = content;
            }
        }
    }
    if (GetVendorType(m_nasHost.vendorSubType) == Module::STORAGE_ENUM::FUSIONSTORAGE) {
        auto localDeviceControl = dynamic_pointer_cast<Module::FusionStorageNas>(m_localDeviceController);
        ret = localDeviceControl->RollBackBySnapShotName(m_snapshotName);
    }

    if (ret != Module::SUCCESS) {
        return ret;
    }
    HCP_Log(DEBUG, MODULE) << "start to Wait for the snapshot restore to complete." << HCPENDLOG;
    ret = WaitingSnapshotRestoreComplete();
    if (ret != Module::SUCCESS) {
        HCP_Log(ERR, MODULE) << "Failed to wait for snapshot restore to complete" << HCPENDLOG;
        return ret;
    }
    HCP_Log(INFO, MODULE) << "Snapshot restore has completed" << HCPENDLOG;
    return Module::SUCCESS;
}

EXTER_ATTACK int SnapshotRestore::PostJob()
{
    m_jobInfoPtr = dynamic_pointer_cast<AppProtect::RestoreJob>(m_jobCommonInfo->GetJobInfo());
    int ret = PostJobInner();
    if (ret == Module::FAILED) {
        ReportJobDetails(EMPTY_LABEL,
            JobLogLevel::type::TASK_LOG_ERROR, SubJobStatus::FAILED, m_jobProgress);
        HCP_Log(ERR, MODULE) << "Exit PostJobInner jobId: " << m_jobInfoPtr->jobId << HCPENDLOG;
        return Module::FAILED;
    }
    ReportJobDetails(EMPTY_LABEL,
        JobLogLevel::type::TASK_LOG_INFO, SubJobStatus::COMPLETED, NUMBER100);
    SetJobToFinish();
    return ret;
}

int SnapshotRestore::PostJobInner() const
{
    return Module::SUCCESS;
}

int SnapshotRestore::InitJobInfo()
{
    m_deviceInfo.username = m_jobInfoPtr->targetEnv.auth.authkey;
    m_deviceInfo.password = m_jobInfoPtr->targetEnv.auth.authPwd;
    m_deviceInfo.port = std::to_string(m_jobInfoPtr->targetEnv.port);
    m_deviceInfo.fileSystemName = m_jobInfoPtr->targetObject.name;
    m_deviceInfo.ipList = m_jobInfoPtr->targetEnv.endpoint;
    TargetObjectExtendInfo extendInfo {};
    if (!JsonHelper::JsonStringToStruct(m_jobInfoPtr->targetObject.extendInfo, extendInfo)) {
        HCP_Log(ERR, MODULE) << "TargetObjectExtendInfo is invaild." << HCPENDLOG;
        return Module::FAILED;
    }
    m_deviceInfo.fileSystemId = extendInfo.fileSystemId;
    m_vstoreId = extendInfo.tenantId;
    m_snapshotName = m_jobInfoPtr->copies[0].name;

    HCP_Log(INFO, MODULE) << "m_deviceInfo.username: " << m_deviceInfo.username << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "m_deviceInfo.port: " << m_deviceInfo.port << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "m_deviceInfo.fileSystemName: " << m_deviceInfo.fileSystemName << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "m_deviceInfo.ipList: " << m_deviceInfo.ipList << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "m_deviceInfo.fileSystemId: " << m_deviceInfo.fileSystemId << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "vstoreId: " << m_vstoreId << HCPENDLOG;
    HCP_Log(INFO, MODULE) << "Snapshot name: " << m_snapshotName << HCPENDLOG;

    m_nasHost.vendorSubType = m_jobInfoPtr->targetEnv.subType;

    return Module::SUCCESS;
}

int SnapshotRestore::CheckExistSnapshot()
{
    HCP_Log(DEBUG, MODULE) << "Enter RestoreSnapshotMainTask CheckExistSnapshot. " << HCPENDLOG;
    // 检查快照是否存在,以及当前ID与数据库中取出的ID是否匹配
    int ret = m_localDeviceController->QuerySnapshot(m_snapshotName, m_snapshotId);
    if (ret != Module::SUCCESS) {
        HCP_Log(ERR, MODULE) << "Query Snapshot Id failed，snapshot name is : " << m_snapshotId
            << " Error code: " << ret << HCPENDLOG;
        return ret;
    }
    if (m_snapshotId == "") {
        HCP_Log(ERR, MODULE) << "snapshot doesn't exist, m_snapshotName is: " << m_snapshotName << HCPENDLOG;
        return Module::FAILED;
    }
    return Module::SUCCESS;
}